
package utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;

import java.lang.reflect.Type;
import java.util.Map;
import java.util.concurrent.TimeUnit;



@Slf4j
public class HttpUtil {

    private static final int MAX_IDLE_CONNECTIONS = 5;
    private static final long KEEP_ALIVE_DURATION_MS = 30_000;
    private static final int CONNECT_TIMEOUT = 500000;
    private static final int READ_TIMEOUT = 100000;
    private static final OkHttpClient client = createOkHttpClient();

    private static OkHttpClient createOkHttpClient() {
        ConnectionPool pool = new ConnectionPool(MAX_IDLE_CONNECTIONS, KEEP_ALIVE_DURATION_MS, TimeUnit.HOURS);

        return new OkHttpClient.Builder()
                .connectionPool(pool)
                .connectTimeout(CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
                .readTimeout(READ_TIMEOUT, TimeUnit.MILLISECONDS)
                .build();
    }

    public static <T> T get(String url, Map<String, String> params, Type clazz)  {
        return get(url, null, params, clazz);
    }

    public static <T> T get(String url, Map<String, String> headers, Map<String, String> params, Type clazz)  {
        HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();

        if (params != null) {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                urlBuilder.addQueryParameter(entry.getKey(), entry.getValue());
            }
        }

        HttpUrl httpUrl = urlBuilder.build();

        Request.Builder builder = new Request.Builder()
                .url(httpUrl);

        return execute(builder, headers, clazz);
    }

    public static <T> T post(String url, Object requestBody, Class<T> clazz)  {
        return post(url, null, requestBody, clazz);
    }

    public static <T> T post(String url, Map<String, String> headers, Object requestBody, Class<T> clazz)  {
        String requestBodyJson = JSON.toJSONString(requestBody);

        log.info("url:{}, body:{}", url, requestBodyJson);

        RequestBody body = RequestBody.create(
                MediaType.parse("application/json; charset=utf-8"), requestBodyJson);

        Request.Builder builder = new Request.Builder()
                .url(url)
                .post(body);

        return execute(builder, headers, clazz);
    }

    public static <T> T postForm(String url, Map<String, String> headers, Map<String, String> params, Class<T> clazz)  {
        FormBody.Builder formBuilder = new FormBody.Builder();
        if (params != null) {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                formBuilder.add(entry.getKey(), entry.getValue());
            }
        }

        Request.Builder builder = new Request.Builder()
                .url(url).post(formBuilder.build());

        return execute(builder, headers, clazz);
    }

    private static <T> T execute(Request.Builder builder, Map<String, String> headers, Type clazz) {
        if (headers != null) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                builder.addHeader(entry.getKey(), entry.getValue());
            }
        }
        Request request = builder.build();
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new RuntimeException("Unexpected code " + response);
            }
            String responseBody = response.body().string();
            if(isJsonString(responseBody)) {
                return JSON.parseObject(responseBody, clazz);
            }
            return (T)convertToType(responseBody, clazz);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static Object convertToType(String str, Type returnType) {
        if (returnType.equals(String.class)) {
            return str;
        } else if (returnType.equals(Integer.class)) {
            return Integer.parseInt(str);
        } else if (returnType.equals(Double.class)) {
            return Double.parseDouble(str);
        } else if (returnType.equals(Boolean.class)) {
            return Boolean.parseBoolean(str);
        }
        return null;
    }

    private static boolean isJsonString(String jsonString) {
        try {
            JSON.parse(jsonString);
            return true;
        } catch (JSONException e) {
            return false;
        }
    }
}